package com.ruyiadmin.springboot.web_api.controllers.framework_controllers;

import com.auth0.jwt.exceptions.TokenExpiredException;
import com.ruyiadmin.springboot.common_infrastructure.constants.framework.Keywords;
import com.ruyiadmin.springboot.common_infrastructure.constants.system.ExceptionMessage;
import com.ruyiadmin.springboot.common_infrastructure.constants.system.WarnningMessage;
import com.ruyiadmin.springboot.web_api.annotations.framework.AllowAnonymous;
import com.ruyiadmin.springboot.web_api.annotations.framework.SimpleRateLimiter;
import com.ruyiadmin.springboot.common_infrastructure.properties.JwtSettings;
import com.ruyiadmin.springboot.common_infrastructure.classes.JwtSecurityAuthentication;
import com.ruyiadmin.springboot.common_infrastructure.classes.JwtSecurityToken;
import com.ruyiadmin.springboot.common_infrastructure.models.ActionResponseResult;
import com.ruyiadmin.springboot.common_infrastructure.exceptions.RuYiAdminCustomException;
import com.ruyiadmin.springboot.common_infrastructure.components.system.RuYiRedisContext;
import com.ruyiadmin.springboot.common_infrastructure.utilities.framework_util.RuYiHashUtil;
import com.ruyiadmin.springboot.common_infrastructure.utilities.system_util.RuYiJwtTokenUtil;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.RequiredArgsConstructor;
import org.apache.commons.lang3.StringUtils;
import org.springframework.boot.context.properties.EnableConfigurationProperties;

import org.springframework.web.bind.annotation.*;

import javax.validation.Valid;
import java.util.UUID;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;

/**
 * <p>
 * 系统Jwt授权服务 前端控制器
 * </p>
 *
 * @author RuYiAdmin
 * @since 2022-07-14
 */
@RestController
@RequestMapping("/JwtSecurityAuthentication")
@Api(tags = "系统Jwt授权服务")
@RequiredArgsConstructor
@EnableConfigurationProperties({JwtSettings.class})
public class JwtSecurityAuthenticationController {

    //region 服务私有属性

    private final JwtSettings jwtSettings;
    private final RuYiRedisContext redisContext;

    //endregion

    //region Jwt获取盐服务

    @GetMapping("/Get")
    @ApiOperation(value = "Jwt获取盐服务")
    @AllowAnonymous
    @SimpleRateLimiter(value = 1.0)
    public ActionResponseResult getSalt(String userName)
            throws RuYiAdminCustomException, ExecutionException, InterruptedException {
        CompletableFuture<ActionResponseResult> future = CompletableFuture.supplyAsync(() -> {
            if (StringUtils.isEmpty(userName)) {
                throw new RuYiAdminCustomException(ExceptionMessage.NotNullUserNameExceptionMessage);
            }
            if (userName.equals(jwtSettings.getDefaultUser())) {
                String salt = UUID.randomUUID().toString();
                String entryStr = RuYiHashUtil.SHA512(jwtSettings.getDefaultPassword() + salt);
                this.redisContext.set(salt, entryStr, jwtSettings.getSaltExpiration());
                return ActionResponseResult.success(salt);
            }
            throw new RuYiAdminCustomException(WarnningMessage.NoContentMessage);
        });
        return future.get();
    }

    //endregion

    //region Jwt身份认证接口

    @PostMapping("/Post")
    @ApiOperation(value = "Jwt身份认证接口")
    @AllowAnonymous
    @SimpleRateLimiter(value = 1.0)
    public ActionResponseResult getJwtSecurityToken(@RequestBody @Valid JwtSecurityAuthentication authentication)
            throws RuYiAdminCustomException, ExecutionException, InterruptedException {
        CompletableFuture<ActionResponseResult> future = CompletableFuture.supplyAsync(() -> {
            String entryStr = null;
            Object value = this.redisContext.get(authentication.getSalt());
            entryStr = value == null ? StringUtils.EMPTY : value.toString();
            if (StringUtils.isEmpty(entryStr)) {
                throw new RuYiAdminCustomException(ExceptionMessage.InvalidSaltExceptionMessage);
            }
            if (authentication.getUserName().equals(this.jwtSettings.getDefaultUser())
                    && authentication.getPassword().equalsIgnoreCase(entryStr)) {
                this.redisContext.del(authentication.getSalt());
                String tokenId = UUID.randomUUID().toString();
                String token = RuYiJwtTokenUtil.createJwtSecurityToken(this.jwtSettings, tokenId);
                //设置RefreshToken有效期
                this.redisContext.set(tokenId, tokenId, (long) 12 * 3 * this.jwtSettings.getTokenExpiration() * 60);
                return ActionResponseResult.success(new JwtSecurityToken(token, tokenId));
            }
            throw new RuYiAdminCustomException(WarnningMessage.NotFindMessage);
        });
        return future.get();
    }

    //endregion

    //region 刷新JwtToken

    @GetMapping("/RefreshToken")
    @ApiOperation(value = "刷新JwtToken")
    @AllowAnonymous
    public ActionResponseResult refreshJwtToken(@RequestHeader(Keywords.AUTHORIZATION) String accessToken,
                                                @RequestHeader(Keywords.REFRESHTOKEN) String refreshToken)
            throws RuYiAdminCustomException, ExecutionException, InterruptedException {
        CompletableFuture<ActionResponseResult> future = CompletableFuture.supplyAsync(() -> {
            try {
                RuYiJwtTokenUtil.verifyToken(this.jwtSettings, accessToken.replace(Keywords.BEARER, ""));
            } catch (TokenExpiredException e) {
                Object value = this.redisContext.get(refreshToken);
                String rtValue = value == null ? StringUtils.EMPTY : value.toString();
                if (!StringUtils.isEmpty(rtValue) && rtValue.equals(refreshToken)) {
                    //删除旧RefreshToken
                    this.redisContext.del(refreshToken);
                    String tokenId = UUID.randomUUID().toString();
                    String token = RuYiJwtTokenUtil.createJwtSecurityToken(this.jwtSettings, tokenId);
                    //设置RefreshToken有效期
                    this.redisContext.set(tokenId, tokenId, (long) 12 * 3 * this.jwtSettings.getTokenExpiration() * 60);
                    return ActionResponseResult.success(new JwtSecurityToken(token, tokenId));
                }
            }
            throw new RuYiAdminCustomException(WarnningMessage.ForbiddenMessage);
        });
        return future.get();
    }

    //endregion

}
